using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;

namespace IGlobalist.Net.RTP
{
    /// <summary>
    /// A data packet consisting of the fixed RTP header, a possibly empty list of contributing 
    /// sources (see below), and the payload data. Some underlying protocols may require an 
    /// encapsulation of the RTP packet to be defined. Typically one packet of the underlying 
    /// protocol contains a single RTP packet, but several RTP packets MAY be contained if 
    /// permitted by the encapsulation method (see Section 11).
    /// </summary>
    public class RTPPacket_
    {
        private int    m_Version        = 2;
        private bool   m_IsMaker        = false;
        private int    m_PayloadType    = 0;
        private short  m_SequenceNumber = 0;
        private int    m_Timestamp      = 0;
        private uint   m_SSRC           = 0;
        private uint[] m_CSRC           = null;
        private byte[] m_Data           = null;

        /// <summary>
        /// Default constructor.
        /// </summary>
        internal RTPPacket_()
        {
        }


        #region static method Parse

        /// <summary>
        /// Parses RTP packet.
        /// </summary>
        /// <param name="buffer">Buffer containing RTP packet.</param>
        /// <param name="size">Number of bytes used in buffer.</param>
        /// <returns>Returns parsed RTP packet.</returns>
        public static RTPPacket Parse(byte[] buffer,int size)
        {
            BufferChunk bc = new BufferChunk(buffer, 0, size);
            RTPPacket packet = new RTPPacket(bc);
            

            return packet;
        }

        #endregion

        #region method Validate

        /// <summary>
        /// Validates RTP packet.
        /// </summary>
        public void Validate()
        {
            // TODO: Validate RTP apcket
        }

        #endregion

        #region method ToByte

        /// <summary>
        /// Stores this packet to the specified buffer.
        /// </summary>
        /// <param name="buffer">Buffer where to store packet.</param>
        /// <param name="offset">Offset in buffer.</param>
        public void ToByte(byte[] buffer,ref int offset)
        {
            /* RFC 3550.5.1 RTP Fixed Header Fields.
             
                The RTP header has the following format:

                0                   1                   2                   3
                0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |V=2|P|X|  CC   |M|     PT      |       sequence number         |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |                           timestamp                           |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |           synchronization source (SSRC) identifier            |
               +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
               |            contributing source (CSRC) identifiers             |
               |                             ....                              |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             
             
               5.3. Available if X bit filled.
                0                   1                   2                   3
                0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |      defined by profile       |           length              |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |                        header extension                       |
               |                             ....                              |

            */

            int cc = 0;
            if(m_CSRC != null){
                cc = m_CSRC.Length;
            }

            // V P X CC
            buffer[offset++] = (byte)(m_Version << 6 | 0 << 5 | cc & 0xF);
            // M PT
            buffer[offset++] = (byte)(Convert.ToInt32(m_IsMaker) << 7 | m_PayloadType & 0x7F);
            // sequence number
            buffer[offset++] = (byte)(m_SequenceNumber >> 8);
            buffer[offset++] = (byte)(m_SequenceNumber & 0xFF);
            // timestamp
            buffer[offset++] = (byte)((m_SequenceNumber >> 24) & 0xFF);
            buffer[offset++] = (byte)((m_SequenceNumber >> 16) & 0xFF);
            buffer[offset++] = (byte)((m_SequenceNumber >>  8) & 0xFF);
            buffer[offset++] = (byte)(m_SequenceNumber & 0xFF);
            // SSRC
            buffer[offset++] = (byte)((m_SSRC >> 24) & 0xFF);
            buffer[offset++] = (byte)((m_SSRC >> 16) & 0xFF);
            buffer[offset++] = (byte)((m_SSRC >>  8) & 0xFF);
            buffer[offset++] = (byte)(m_SSRC & 0xFF);
            // CSRCs
            if(m_CSRC != null){
                foreach(int csrc in m_CSRC){
                    buffer[offset++] = (byte)((csrc >> 24) & 0xFF);
                    buffer[offset++] = (byte)((csrc >> 16) & 0xFF);
                    buffer[offset++] = (byte)((csrc >>  8) & 0xFF);
                    buffer[offset++] = (byte)(csrc & 0xFF);
                }
            }
            // X
            Array.Copy(m_Data,0,buffer,offset,m_Data.Length);
            offset += m_Data.Length;
        }

        #endregion

        #region override method ToString

        /// <summary>
        /// Returns this packet info as string.
        /// </summary>
        /// <returns>Returns packet info.</returns>
        public override string ToString()
        {
            StringBuilder retVal = new StringBuilder();
            retVal.Append("----- RTP Packet\r\n");
            retVal.Append("Version: " + m_Version.ToString() + "\r\n");
            retVal.Append("IsMaker: " + m_IsMaker.ToString() + "\r\n");
            retVal.Append("PayloadType: " + m_PayloadType.ToString() + "\r\n");
            retVal.Append("SeqNo: " + m_SequenceNumber.ToString() + "\r\n");
            retVal.Append("Timestamp: " + m_Timestamp.ToString() + "\r\n");
            retVal.Append("SSRC: " + m_SSRC.ToString() + "\r\n");
            retVal.Append("Data: " + m_Data.Length + " bytes.\r\n");

            return retVal.ToString();
        }

        #endregion


        #region method ParseInternal

        /// <summary>
        /// Parses RTP packet from the specified buffer.
        /// </summary>
        /// <param name="buffer">Buffer containing RTP packet.</param>
        /// <param name="size">Number of bytes used in buffer.</param>
        private void ParseInternal(byte[] buffer,int size)
        {
            /* RFC 3550.5.1 RTP Fixed Header Fields.
             
                The RTP header has the following format:

                0                   1                   2                   3
                0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |V=2|P|X|  CC   |M|     PT      |       sequence number         |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |                           timestamp                           |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |           synchronization source (SSRC) identifier            |
               +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
               |            contributing source (CSRC) identifiers             |
               |                             ....                              |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             
             
               5.3. Available if X bit filled.
                0                   1                   2                   3
                0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |      defined by profile       |           length              |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |                        header extension                       |
               |                             ....                              |

            */

            int offset = 0;

            // V
            m_Version = buffer[offset] >> 6;
            // P
            bool isPadded  = Convert.ToBoolean((buffer[offset] >> 5) & 0x1);
            // X
            bool hasExtention = Convert.ToBoolean((buffer[offset] >> 4) & 0x1);
            // CC
            int csrcCount = buffer[offset++] & 0xF;
            // M
            m_IsMaker = Convert.ToBoolean(buffer[offset] >> 7);
            // PT
            m_PayloadType = buffer[offset++] & 0x7F;
            // sequence number
            m_SequenceNumber = (short)(buffer[offset++] << 8 | buffer[offset++]);
            // timestamp
            m_Timestamp = buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++];
            // SSRC
            m_SSRC = (uint)(buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]);
            // CSRC
            m_CSRC = new uint[csrcCount];
            for(int i=0;i<csrcCount;i++){
                m_CSRC[i] = (uint)(buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]);
            }
            // X
            if(hasExtention){
                // Skip extention
                offset++;
                offset += buffer[offset];
            }

            // TODO: Padding

            // Data
            m_Data = new byte[size - offset];
            Array.Copy(buffer,offset,m_Data,0,m_Data.Length);
        }

        #endregion


        #region Properties Implementation

        /// <summary>
        /// Gets RTP version.
        /// </summary>
        public int Version
        {
            get{ return m_Version; }
        }

        /// <summary>
        /// Gets if packet is padded to some bytes boundary.
        /// </summary>
        public bool IsPadded
        {
            get{ return false; }
        }

        /// <summary>
        /// Gets maker bit. The usage of this bit depends on payload type.
        /// </summary>
        public bool IsMaker
        {
            get{ return m_IsMaker; }

            set{ m_IsMaker = value; }
        }

        /// <summary>
        /// Gets payload type.
        /// </summary>
        /// <exception cref="ArgumentException">Is raised when invalid value is passed.</exception>
        public int PayloadType
        {
            get{ return m_PayloadType; }

            set{
                if(value < 0 || value > 128){
                    throw new ArgumentException("Payload value must be >= 0 and <= 128.");
                }

                m_PayloadType = value; 
            }
        }

        /// <summary>
        /// Gets or sets RTP packet sequence number.
        /// </summary>
        /// <exception cref="ArgumentException">Is raised when invalid value is passed.</exception>
        public short SequenceNumber
        {
            get{ return m_SequenceNumber; }

            set{
                if(value < 1){
                    throw new ArgumentException("SequenceNumber value must be >= 1.");
                }

                m_SequenceNumber = value; 
            }
        }
        
        /// <summary>
        /// Gets sets packet timestamp. 
        /// </summary>
        /// <exception cref="ArgumentException">Is raised when invalid value is passed.</exception>
        public int Timestamp
        {
            get{ return m_Timestamp; }

            set{
                if(value < 1){
                    throw new ArgumentException("Timestamp value must be >= 1.");
                }

                m_Timestamp = value;
            }
        }

        /// <summary>
        /// Gets or sets synchronization source ID.
        /// </summary>
        /// <exception cref="ArgumentException">Is raised when invalid value is passed.</exception>
        public uint SSRC
        {
            get{ return m_SSRC; }

            set{
                if(value < 1){
                    throw new ArgumentException("SSRC value must be >= 1.");
                }

                m_SSRC = value; 
            }
        }

        /// <summary>
        /// Gets or sets the contributing sources for the payload contained in this packet.
        /// Value null means none.
        /// </summary>
        public uint[] CSRC
        {
            get{ return m_CSRC; }

            set{ m_CSRC = value; }
        }

        /// <summary>
        /// Gets SSRC + CSRCs as joined array.
        /// </summary>
        public uint[] Sources
        {
            get{
                uint[] retVal = new uint[1];
                if(m_CSRC != null){
                    retVal = new uint[1 + m_CSRC.Length];
                }
                retVal[0] = m_SSRC;
                Array.Copy(m_CSRC,retVal,m_CSRC.Length);

                return retVal; 
            }
        }

        /// <summary>
        /// Gets or sets RTP data. Data must be encoded with PayloadType encoding.
        /// </summary>
        /// <exception cref="ArgumentNullException">Is raised when null value is passed.</exception>
        public byte[] Data
        {
            get{ return m_Data; }

            set{
                if(value == null){
                    throw new ArgumentNullException("Data");
                }

                m_Data = value; 
            }
        }

        #endregion

    }


    #region Public Enumerations
    /// <summary>
    /// Enumeration for Rtp PayloadTypes using values per RFC 1890.  PayloadType was previously used to tightly couple the data stream to an
    /// exact data type, but this has been falling out of favor as payload types diverge and systems such as DirectShow and QuickTime carry
    /// the media type in much greater detail in band with the data.
    /// 
    /// We use the terms 'dynamicVideo' and 'dynamicAudio', specified at the end of the dynamic band of PayloadTypes and then include a
    /// DirectShow AM_MEDIA_TYPE structure in the Rtp header extension of the first packet in a frame.  See the packet format on the website
    /// for detailed information on how this is transmitted.
    /// </summary>
    /// <example>
    ///       PT         encoding      audio/video    clock rate    channels
    ///                  name          (A/V)          (Hz)          (audio)
    ///             _______________________________________________________________
    ///             0          PCMU          A              8000          1
    ///             1          1016          A              8000          1
    ///             2          G721          A              8000          1
    ///             3          GSM           A              8000          1
    ///             4          unassigned    A              8000          1
    ///             5          DVI4          A              8000          1
    ///             6          DVI4          A              16000         1
    ///             7          LPC           A              8000          1
    ///             8          PCMA          A              8000          1
    ///             9          G722          A              8000          1
    ///             10         L16           A              44100         2
    ///             11         L16           A              44100         1
    ///             12         unassigned    A
    ///             13         unassigned    A
    ///             14         MPA           A              90000        (see text)
    ///             15         G728          A              8000          1
    ///             16--23     unassigned    A
    ///             24         unassigned    V
    ///             25         CelB          V              90000
    ///             26         JPEG          V              90000
    ///             27         unassigned    V
    ///             28         nv            V              90000
    ///             29         unassigned    V
    ///             30         unassigned    V
    ///             31         H261          V              90000
    ///             32         MPV           V              90000
    ///             33         MP2T          AV             90000
    ///             34--71     unassigned    ?
    ///             72--76     reserved      N/A            N/A           N/A
    ///             77--95     unassigned    ?
    ///             96--127    dynamic       ?
    /// </example>
    public enum PayloadType : byte
    {
        PCMU = 0, PT1016, G721, GSM,
        DVI4 = 5,
        LPC = 7, PCMA, G722, L16,
        MPA = 14, G728,
        CelB = 25, JPEG,
        nv = 28,
        H261 = 31, MPV, MP2T,
        // 96-127 are intended for dynamic assignment
        Chat = 96,
        xApplication2,
        xApplication3,
        xApplication4,
        xApplication5,
        xApplication6,
        xApplication7,
        xApplication8,
        xApplication9,
        xApplication10,
        Venue1 = 106,
        Venue2,
        Venue3,
        Venue4,
        Venue5,
        Venue6,
        Venue7,
        Venue8,
        Venue9,
        GroupChat = 115,
        FileTransfer = 116,
        ManagedDirectX = 117,
        Whiteboard = 118,
        SharedBrowser = 119,
        RichTextChat = 120,
        RTDocument = 121,               // Serialization of an RTDocument object, network protocol TBD
        PipecleanerSignal = 122,        // Diagnostic signal used by the Pipecleaner applications to test connectivity between nodes
        Test = 123,                     // Used for test cases
        FEC = 124,                      // Identifies a packet as containing Forward Error Correction information
        dynamicPresentation = 125,      // Obsolete, being replaced by RTDocument -- lifetime TBD
        dynamicVideo = 126,             // A video signal.  The format of the video signal is embedded in the data stream itself
        dynamicAudio = 127              // An audio signal.  The format of the audio signal is embedded in the data stream itself
    }
    #endregion

    #region RTPPacketBase

    /// <summary>
    /// The standard Rtp packet
    /// 
    /// TODO - we may need to re-add support for CSRCs and HeaderExtensions, although header
    /// extensions could just as easily be implemented as payload headers
    /// </summary>
    public class RTPPacketBase
    {
        #region Statics

        internal const int RTP_HEADER_SIZE = SSRC_INDEX + SSRC_SIZE;

        private const int VPXCC_SIZE = 1;
        private const int MPT_SIZE = 1;
        private const int SEQ_SIZE = 2;
        private const int TS_SIZE = 4;
        protected const int SSRC_SIZE = 4;

        private const int VPXCC_INDEX = 0;
        private const int MPT_INDEX = VPXCC_INDEX + VPXCC_SIZE;
        private const int SEQ_INDEX = MPT_INDEX + MPT_SIZE;
        protected const int TS_INDEX = SEQ_INDEX + SEQ_SIZE;
        protected const int SSRC_INDEX = TS_INDEX + TS_SIZE;


        /// <summary>
        ///  Cast operator for forming a BufferChunk from an RTPPacketBase.
        /// </summary>
        public static explicit operator BufferChunk(RTPPacketBase packet)
        {
            Debug.Assert(packet.buffer.Length == (packet.HeaderSize + packet.PayloadSize));
            return packet.buffer;
        }


        /// <summary>
        ///  Cast operator for forming a BufferChunk from an RTPPacketBase.
        /// </summary>
        public static explicit operator RTPPacketBase(BufferChunk buffer)
        {
            return new RTPPacketBase(buffer);
        }


        #endregion Statics

        #region Members

        /// <summary>
        ///  Buffer to contain the raw data
        /// </summary>
        protected BufferChunk buffer;

        #endregion Members

        #region Constructors

        /// <summary>
        /// Creates a max size packet
        /// </summary>
        internal RTPPacketBase() : this(Rtp.MAX_PACKET_SIZE) { }

        /// <summary>
        /// Creates a packet of the given size
        /// </summary>
        internal RTPPacketBase(int packetSize)
        {
            buffer = new BufferChunk(new byte[packetSize]);
            Reset();
        }

        /// <summary>
        /// Create a packet from an existing buffer
        /// </summary>
        /// <param name="buffer"></param>
        internal RTPPacketBase(BufferChunk buffer)
        {
            ValidateBuffer(buffer);

            this.buffer = buffer;
        }

        /// <summary>
        /// Create a packet from an existing packet
        /// </summary>
        /// <param name="packet"></param>
        internal RTPPacketBase(RTPPacketBase packet)
        {
            buffer = packet.buffer;
        }


        #endregion

        #region Internal

        /// <summary>
        /// Marker reserved for payload/protocol specific information.
        /// </summary>
        internal bool Marker
        {
            get { return ((buffer[MPT_INDEX] & 128) == 128); }

            set
            {
                if (value)
                {
                    // Set it
                    buffer[MPT_INDEX] |= (byte)(128);
                }
                else
                {
                    // Clear the bit
                    buffer[MPT_INDEX] ^= (byte)(buffer[MPT_INDEX] & 128);
                }
            }
        }

        /// <summary>
        /// The type of data contained in the packet
        /// </summary>
        internal PayloadType PayloadType
        {
            get { return (PayloadType)(buffer[MPT_INDEX] & 127); }

            set
            {
                if ((int)value > 127)
                {
                    throw new ArgumentOutOfRangeException("MSR.LST.Rtp.RTPPacketBase.PayloadType" +
                        " is a seven bit structure, and can hold values between 0 and 127");
                }

                // Preserve most significant bit
                buffer[MPT_INDEX] = (byte)(buffer[MPT_INDEX] & 128);
                buffer[MPT_INDEX] += (byte)value;
            }
        }

        /// <summary>
        /// Sequence number of the packet, used to keep track of the order packets were sent in
        /// 
        /// public because it is used by NetworkDumper
        ///</summary>
        public ushort Sequence
        {
            get { return buffer.GetUInt16(SEQ_INDEX); }
            set { buffer.SetUInt16(SEQ_INDEX, value); }
        }

        /// <summary>
        /// According to the spec - timestamp is the sampling instant for the first octet of the
        /// media data in a packet, and is used to schedule playout of the media data.
        /// 
        /// In our implementation, it is an incrementing counter used to group packets into a frame
        /// </summary>
        internal virtual uint TimeStamp
        {
            get { return buffer.GetUInt32(TS_INDEX); }
            set { buffer.SetUInt32(TS_INDEX, value); }
        }

        /// <summary>
        /// Synchronization source used to identify streams within a session
        /// 
        /// public because it is used by NetworkDumper
        /// </summary>
        public uint SSRC
        {
            get { return buffer.GetUInt32(SSRC_INDEX); }
            set { buffer.SetUInt32(SSRC_INDEX, value); }
        }

        public uint[] Sources
        {
            get{
                // CC
                int offset = VPXCC_INDEX;
                int csrcCount = buffer[offset] & 0xF;
                uint[] m_CSRC = new uint[csrcCount];
                offset = SSRC_INDEX + SSRC_SIZE;
                for (int i = 0; i < csrcCount; i++)
                {
                    m_CSRC[i] = (uint)(buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]);
                }
                return m_CSRC;
            }

        }



        /// <summary>
        /// Payload data of the RTPPacket
        /// </summary>
        public BufferChunk Payload
        {
            set
            {
                // Make sure they haven't tried to add more data than we can handle
                if (value.Length > MaxPayloadSize)
                {
                    throw new ArgumentOutOfRangeException("value", value.Length, "The maximum " +
                        "payload for this packet is: " + MaxPayloadSize);
                }

                // Reset Buffer to just after the header because packets are re-used and so that
                // operator+ works properly when copying the payload
                buffer.Reset(0, HeaderSize);
                buffer += value;
            }
            get
            {
                return buffer.Peek(HeaderSize, PayloadSize);
            }
        }

        internal void AppendPayload(Int32 data)
        {
            buffer += data;
        }

        internal void AppendPayload(BufferChunk data)
        {
            buffer += data;
        }

        internal void AppendPayload(IntPtr ptr, int length)
        {
            buffer.CopyFrom(ptr, length);
        }

        /// <summary>
        /// How much payload data can this packet accept
        /// 
        /// Be sure and set all of the header information before making this call otherwise it will
        /// be incorrect.
        /// </summary>
        internal int MaxPayloadSize
        {
            get { return buffer.Buffer.Length - HeaderSize; }
        }

        /// <summary>
        /// Release the BufferChunk held by this packet so it can be reused outside the scope of this packet.
        /// </summary>
        /// <returns></returns>
        internal BufferChunk ReleaseBuffer()
        {
            BufferChunk ret = buffer;
            buffer = null;

            return ret;
        }


        internal virtual int HeaderSize
        {
            get { return RTP_HEADER_SIZE; }
        }


        internal BufferChunk Buffer
        {
            get { return buffer; }
        }


        internal virtual int PayloadSize
        {
            get
            {
                int size = buffer.Length - HeaderSize;

                Debug.Assert(size >= 0);

                return size;
            }
            set
            {
                buffer.Reset(0, HeaderSize + value);
            }
        }


        internal virtual void Reset()
        {
            buffer.Reset(0, HeaderSize);

            // Initialize the first byte: V==2, P==0, X==0, CC==0
            buffer[VPXCC_INDEX] = (byte)(Rtp.VERSION << 6);
        }

        #endregion Internal

        #region Private

        /// <summary>
        /// Make sure the provided buffer might be a real Rtp Packet (version == 2)
        /// </summary>
        private void ValidateBuffer(BufferChunk buffer)
        {
            int version = buffer[VPXCC_INDEX] >> 6;

            if (version != Rtp.VERSION)
                throw new InvalidRTPPacketException(string.Format("Invalid version: {0}, current: {1}",
                    version, Rtp.VERSION));
        }

        #endregion Private

    }


    #endregion RTPPacketBase

    #region RTPPacket

    /// <summary>
    /// RTPPacket is based on RFC 1889.  This class knows how to form a byte array for sending out over the network and how to turn a byte array into Rtp fields and a payload.
    /// It is mean to be used as a translation mechanism from bytes to structure and vice versa.  This is a lower level class exposed only for use by
    /// applications that want intiment details about an individual Rtp packet or who want to provide their own transport mechanism.  Applications
    /// that simply want to send/receive real time data over IP Multicast should instead use RtpSender / RtpListener which handles all
    /// aspects of network transport and framing (AKA breaking/assembling large datasets into packet sized chunks).
    /// 
    /// There is a small amount of Rtp protocol intelligence in the class when you use the Next methods.  The Next methods assume you are working
    /// on RTPPackets in a series and will perform helper functions such as compare Sequence numbers for linearness and NextPayload increments
    /// the Sequence number between new packets.
    /// 
    /// This implementation has no support for CSRC identifiers.
    /// 
    /// 
    ///       The Rtp header has the following format:
    ///
    ///0                   1                   2                   3
    ///0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    ///|V=2|P|X|  CC   |M|     PT      |       sequence number         |
    ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    ///|                           timestamp                           |
    ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    ///|           synchronization source (SSRC) identifier            |
    ///+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
    ///|            contributing source (CSRC) identifiers             |
    ///|                             ....                              |
    ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    /// </summary>
    public class RTPPacket : RTPPacketBase
    {
        #region Statics

        /// <summary>
        /// We use a fixed size header extension
        /// </summary>
        private const int HEADER_EXTENSIONS_SIZE = PACKETS_IN_FRAME_SIZE +
                                                   FRAME_INDEX_SIZE +
                                                   FEC_INDEX_SIZE;

        private const int PACKETS_IN_FRAME_SIZE = 2;
        private const int FRAME_INDEX_SIZE = 2;
        private const int FEC_INDEX_SIZE = 2;

        #endregion Statics

        #region Constructors

        internal RTPPacket() : base() { }

        internal RTPPacket(int packetSize) : base(packetSize) { }

        internal RTPPacket(BufferChunk buffer) : base(buffer) { }

        internal RTPPacket(RTPPacketBase packet) : base(packet) { }


        #endregion

        #region Internal

        internal ushort PacketsInFrame
        {
            get { return Buffer.GetUInt16(PacketsInFrame_Index); }
            set { Buffer.SetUInt16(PacketsInFrame_Index, value); }
        }

        internal ushort FrameIndex
        {
            get
            {
                return Buffer.GetUInt16(FrameIndex_Index);
            }
            set
            {
                Buffer.SetUInt16(FrameIndex_Index, value);
            }
        }

        internal ushort FecIndex
        {
            get
            {
                return Buffer.GetUInt16(FecIndex_Index);
            }
            set
            {
                Buffer.SetUInt16(FecIndex_Index, value);
            }
        }


        internal override int HeaderSize
        {
            get
            {
                return base.HeaderSize + HEADER_EXTENSIONS_SIZE;
            }
        }


        #endregion Internal

        #region Private

        private int PacketsInFrame_Index
        {
            get { return base.HeaderSize; }
        }

        private int FrameIndex_Index
        {
            get { return PacketsInFrame_Index + PACKETS_IN_FRAME_SIZE; }
        }

        private int FecIndex_Index
        {
            get { return FrameIndex_Index + FRAME_INDEX_SIZE; }
        }


        #endregion Private
    }

    #endregion RTPPacket

    #region RTPPacketFec

    /// <summary>
    /// RTPPacketFec is a forward error correction packet.  It is used to provide error correction
    /// for data packets that may become lost.
    /// 
    /// It has a fixed payload type PayloadType.FEC
    /// The normal Rtp Timestamp has been repurposed in order to save bytes.  It is split into...
    /// 
    /// FecIndex - the index of this packet within the fec packet[].  The size of the fec packet[]
    /// is either determined by the constant fec ratio, or the percent coverage across a frame.
    /// 
    /// DataRangeMin - the starting data packet sequence number for which this packet provides
    /// coverage.
    /// 
    /// PacketsInFrame - how many packets are in a frame.  Used in the event that no data packets
    /// are received, but enough fec packets arrive to recover the data.
    /// 
    /// 0                   1                   2                   3
    /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    ///|V=2|P|X|  CC   |M|    PT.FEC   |       sequence number         |
    ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    ///|          DataRangeMin         |         PacketsInFrame        |
    ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    ///|           synchronization source (SSRC) identifier            |
    ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    ///|           FecIndex            |
    ///+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
    /// </summary>
    public class RTPPacketFec : RTPPacketBase
    {
        #region Statics

        // This is a hack.  The general idea is that we don't want to fragment an Fec Packet.
        // In order to prevent fragmentation, we have to limit an Rtp Packet to a known size, so
        // there is room for the Fec Packet overhead.  When we add support for CSRCs or Header
        // extensions, this value will be incorrect.  And we won't have allocated enough space for
        // the Fec Packet.  JVE 6/16/2004
        internal const int HEADER_SIZE_HACK = RTPPacketBase.RTP_HEADER_SIZE +
                                              HEADER_OVERHEAD_SIZE +
                                              2 /*CFec.SIZE_OVERHEAD*/;

        /// <summary>
        /// The 4 bytes of Timestamp have been re-purposed, but we needed 2 extra bytes for header
        /// </summary>
        private const int HEADER_OVERHEAD_SIZE = 2;

        #endregion Statics

        #region Constructors

        internal RTPPacketFec()
            : base()
        {
            PayloadType = PayloadType.FEC;
        }

        internal RTPPacketFec(RTPPacketBase packet)
            : base(packet)
        {
            if (PayloadType != PayloadType.FEC)
            {
                throw new ArgumentException("The provided packet is not an fec packet!");
            }
        }


        #endregion Constructors

        #region Internal

        internal override int HeaderSize
        {
            get
            {
                int size = base.HeaderSize;

                // See HEADER_SIZE_HACK comments
                Debug.Assert(size == RTPPacketBase.RTP_HEADER_SIZE);

                size += HEADER_OVERHEAD_SIZE;

                return size;
            }
        }


        internal override uint TimeStamp
        {
            get
            {
                throw new InvalidOperationException("RTPPacketFec does not support the TimeStamp" +
                    " property.");
            }
            set
            {
                throw new InvalidOperationException("RTPPacketFec does not support the TimeStamp" +
                    " property.");
            }
        }


        internal override void Reset()
        {
            buffer.Clear();
            base.Reset();
            PayloadType = PayloadType.FEC;
        }


        internal ushort DataRangeMin
        {
            get { return Buffer.GetUInt16(DataRangeMin_Index); }
            set { Buffer.SetUInt16(DataRangeMin_Index, value); }
        }


        internal ushort PacketsInFrame
        {
            get { return Buffer.GetUInt16(PacketsInFrame_Index); }
            set { Buffer.SetUInt16(PacketsInFrame_Index, value); }
        }


        internal ushort FecIndex
        {
            get { return Buffer.GetUInt16(FecIndex_Index); }
            set { Buffer.SetUInt16(FecIndex_Index, value); }
        }


        #endregion Internal

        #region Private

        private int DataRangeMin_Index
        {
            get { return TS_INDEX; }
        }

        private int PacketsInFrame_Index
        {
            get { return DataRangeMin_Index + 2; }
        }

        private int FecIndex_Index
        {
            get { return SSRC_INDEX + SSRC_SIZE; }
        }

        #endregion Private
    }

    #endregion RTPPacketFec

    #region Exception Classes
    /// <summary>
    /// OutOfOrder exception is thrown when issues are found with the Sequence or TimeStamp where they don't match up with the expected values an
    /// individual packet in a stream of Rtp packets should have.
    /// 
    /// Note that this exception is also thrown by the RTPPacket class when using the RTPPacket.Next() method.
    /// </summary>
    public class PacketOutOfSequenceException : ApplicationException
    {
        public int LostPackets = 0;
        public PacketOutOfSequenceException()
        {
        }
        public PacketOutOfSequenceException(string message)
            : base(message)
        {
        }
        public PacketOutOfSequenceException(string message, int lostPackets)
            : base(message)
        {
            LostPackets = lostPackets;
        }
        public PacketOutOfSequenceException(string message, Exception inner)
            : base(message, inner)
        {
        }
    }

    /// <summary>
    /// InvalidRTPPacket exception is thrown when an Rtp packet has invalid contents either due to an invalid Rtp header or due to unexpected
    /// data in a stream such as a HeaderExtension where none should be present or an invalid TimeStamp value in an Rtp Frame.
    /// </summary>
    /// <remarks>
    /// This can be caused by other traffic than just Rtp on an IPEndPoint or Rtp traffic from another sending program that doesn't follow the same
    /// framing rules.  It shouldn't be caused by packet data corruption on UDP streams since each UDP packet is CRC32 validated before being accepted
    /// and passed up by System.Net.Sockets.  It also should only be an issue if there is SSRC &amp; PayloadType collision between different sending
    /// applications, which should be rare if SSRCs are chosen according to the RFC 1889 specification.
    /// 
    /// Perhaps we should rename this to be consistent with some form of 'streaming/framing error'.  If we get a true 'invalid Rtp Packet' error, it's
    /// probably due to non-Rtp data being on the IP address and this should be filtered rather than Excepted.  Perhaps we should republish this an
    /// event like was done with OutOfOrder so that non-Rtp traffic could be detected and logged.
    /// </remarks>
    public class InvalidRTPPacketException : ApplicationException
    {
        public InvalidRTPPacketException()
        {
        }
        public InvalidRTPPacketException(string message)
            : base(message)
        {
        }
        public InvalidRTPPacketException(string message, Exception inner)
            : base(message, inner)
        {
        }
    }
    #endregion
}
